package com.lsh.streamgd.controller;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.lsh.streamgd.beans.CsvUploadInfo;
import com.lsh.streamgd.beans.TaskConfig;
import com.lsh.streamgd.dao.TaskRecord;
import com.lsh.streamgd.db.models.Agent;
import com.lsh.streamgd.db.models.User;
import com.lsh.streamgd.db.service.AgentService;
import com.lsh.streamgd.db.service.UserService;
import com.lsh.streamgd.excelExtractor.*;
import com.lsh.streamgd.http.HttpRequest;
import com.lsh.streamgd.http.NetWorkContext;
import com.lsh.streamgd.sparql.Upload;
import com.lsh.streamgd.utils.*;
import org.apache.any23.rdf.RDFUtils;
import org.openrdf.model.URI;
import org.openrdf.model.impl.URIImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

@Controller
public class DBController {

    private static Logger logger = LoggerFactory.getLogger("DBController");

    @Resource
    private UserService userService;

    @Resource
    private AgentService agentService;

    private String getConfigFilePath() {
        return Settings.webappRoot + "taskConfig\\config.rdf";
    }

    @RequestMapping(value = "login", produces = "application/json;charset=utf-8")
    @ResponseBody
    public String connectDB(HttpServletRequest request, String userName, String passWord) {
        if (StringUtil.hasBlank(userName, passWord)) {
            return Constants.AjaxCode.PARAMLOSS;
        }
        User user = new User();
        user.setPsswrd(passWord);
        if (userName.contains("@")) {
            user.setEmail(userName);
        }else{
            user.setMobile(userName);
        }
        List<User> users = userService.userLogin(user);
        if (users != null && users.size() == 1) {
            request.getSession().setAttribute("user", users.get(0).getName());
            return Constants.AjaxCode.SUCCESS;
        }else{
            return Constants.AjaxCode.FAILED;
        }
    }

    @RequestMapping(value = "connectDB", produces = "application/json;charset=utf-8")
    @ResponseBody
    public String connectDB(HttpServletRequest request, TaskConfig taskConfig) {
        logger.debug("\n 收到连接数据库请求参数:{}", taskConfig);
        if (taskConfig == null || StringUtil.hasBlank(taskConfig.getDbName(), taskConfig.getIpAddress(),
                taskConfig.getPortNo(), taskConfig.getUserName(), taskConfig.getCompanyName())) {
            return Constants.AjaxCode.PARAMLOSS;
        }
        Map<String, List<String[]>> map = null;
        if (taskConfig.getDbType() == Constants.DBType.MYSQL) {
            map = DBConnecter.connectDB_MySql(taskConfig);
        } else if (taskConfig.getDbType() == Constants.DBType.POSTGRES) {
            map = DBConnecter.connectDB_PostGre(taskConfig);
        } else if (taskConfig.getDbType() == Constants.DBType.ORACLE) {
            map = DBConnecter.connectDB_Oracle(taskConfig);
        } else {
            return Constants.AjaxCode.DB_NOTSUPPORT;
        }
        if (map == null) return Constants.AjaxCode.ERROR_DBCONNECTION;
        request.getSession().setAttribute("TaskConfig", taskConfig);
        request.getSession().setAttribute("tables", map);
        return Constants.AjaxCode.SUCCESS;
    }

    @RequestMapping(value = "setWhiteNameList", produces = "application/json;charset=utf-8")
    @ResponseBody
    public String setWhiteNameList(HttpServletRequest request, String whiteNameColumns) {
        logger.debug("\n===>received the selection of white columns json:{}", whiteNameColumns);
        if (!StringUtil.isBlank(whiteNameColumns)) {
            TaskConfig taskConfig = (TaskConfig) request.getSession().getAttribute("TaskConfig");
            if (taskConfig != null) {
                taskConfig.setWhiteNameColumns(whiteNameColumns);
                taskConfig.setRootPath(Settings.webappRoot);
                Map<String[], List<String[]>> openedTableColumn = TaskUtil.setWhiteNameList(taskConfig, false);
                if (openedTableColumn != null) {
                    request.getSession().setAttribute("openedTableColumn", openedTableColumn);
                    return Constants.AjaxCode.SUCCESS;
                }
            }
        }
        return Constants.AjaxCode.ERROR_UNKOWN;
    }

    @RequestMapping(value = "/insertComments")
    @ResponseBody
    public String insertComments(HttpServletRequest request, String uris, String comments) {
        TaskConfig taskConfig = (TaskConfig) request.getSession().getAttribute("TaskConfig");
        if (taskConfig != null) {
            taskConfig.setUris(uris);
            taskConfig.setComments(comments);
            if (TaskUtil.insertComments(taskConfig)) {
                request.setAttribute("TaskConfig", taskConfig);
                return Constants.AjaxCode.SUCCESS;
            }
        }
        return Constants.AjaxCode.ERROR_UNKOWN;
    }

    @RequestMapping(value = "/upLoad")
    @ResponseBody
    public String upLoad(HttpServletRequest request) {
        TaskConfig taskConfig = (TaskConfig) request.getSession().getAttribute("TaskConfig");
        if (taskConfig != null) {
            if (TaskUtil.upLoad(taskConfig, false)) {
                taskConfig.setStatus(1); //默认设为启动
                TaskRecord.addRecord(taskConfig, getConfigFilePath());
                request.getSession().removeAttribute("TaskConfig");
                request.getSession().removeAttribute("tables");
                request.getSession().removeAttribute("openedTableColumn");
                return Constants.AjaxCode.SUCCESS;
            }
        }
        return Constants.AjaxCode.ERROR_UNKOWN;
    }

    @RequestMapping(value = "/csvUpload")
    @ResponseBody
    public String csvUpLoad(CsvUploadInfo csvUploadInfo,@RequestParam MultipartFile csvFile,HttpServletRequest request) {
        logger.info("\n=====[  csvUpload  ]=====\n{}", csvUploadInfo);
        String nick = (String) request.getSession().getAttribute("user");
        if (nick == null) {
            return Constants.AjaxCode.NO_AUTH;
        }
        if (StringUtil.hasBlank(csvUploadInfo.getClassLabelEn(), csvUploadInfo.getClassLabelZh())) {
            return Constants.AjaxCode.PARAMLOSS;
        }
        try {
            String baseUri = Settings.graphDomain + Constants.PREFIX_ONTOLOGY + "personal"; //uri:/ontology/personal
            String instanceUri = Settings.graphDomain + Constants.PREFIX_RESOURCE + "personal"; //uri:/resource/personal
            String classUri = baseUri + "/" + nick.substring(0, 1).toUpperCase() + nick.substring(1) + "_" + csvUploadInfo.getClassLabelEn(); //uri:/ontology/personal/NickName_ClassName
            String encode = EnCode.getNameByValue(csvUploadInfo.getEnCode());

            File root = new File(Settings.graphPath);
            if (!root.isDirectory()) {
                root.mkdir();
            }

            String director = Settings.graphPath + File.separator + "temp";
            File dirname = new File(director);
            if (!dirname.isDirectory()) { //目录不存在
                dirname.mkdir(); //创建目录
            }

            String temp_file = Settings.graphPath + File.separator + "temp" + File.separator + nick + "_" + (new Date()).getTime() + csvFile.getOriginalFilename();

            InputStream ins = csvFile.getInputStream();
            FileOutputStream fos = new FileOutputStream(temp_file);

            byte[] b = new byte[1024];
            while((ins.read(b)) != -1){
                fos.write(b);
            }

            ins.close();
            fos.close();

            List<File> csvFiles = new ArrayList<>();
            csvFiles.add(new File(temp_file));

            ExtractionContext ctx = new ExtractionContext("CSV", new URIImpl(baseUri));
            ctx.setClassURI(classUri);
            ctx.setInstanceURI(instanceUri);
            ctx.setClassLabel(csvUploadInfo.getClassLabelZh());
            ctx.setBaseURI(baseUri);
            ctx.setVersion(csvUploadInfo.getVersion());
            ctx.setCreator(csvUploadInfo.getCreator());
            ctx.setTitle(nick);
            ctx.setLang(csvUploadInfo.getLang() == Lang.EN.value ? Lang.EN.name : Lang.ZH.name);
            ctx.setCode(encode);
            ctx.setNickName(nick);

            URI[] headerURI;
            String[] headers = null, headers_zh = null;
            int start_index = 0;

            //英文表头不为空
            if (!StringUtil.isBlank(csvUploadInfo.getHeaderEn())) {
                if (!StringUtil.isBlank(csvUploadInfo.getHeaderZh())) {
                    //中英文表头都不为空
                    headers = csvUploadInfo.getHeaderEn().split(",");
                    headers_zh = csvUploadInfo.getHeaderZh().split(",");
                } else {
                    //中文表头从文件获取，英文表头从页面获取
                    headers = csvUploadInfo.getHeaderEn().split(",");
                    //获取第一行数据
                    InputStreamReader isr = new InputStreamReader(new FileInputStream(csvFiles.get(0)), encode);
                    BufferedReader br = new BufferedReader(isr);
                    String lineTxt = null;
                    while((lineTxt = br.readLine()) != null){
                        headers_zh = CSVUtils.parseLine(lineTxt).toArray(new String[]{});
                        start_index = lineTxt.getBytes().length + 2;
                        break;
                    }
                    br.close();
                    isr.close();
                }
            } else {
                if (!StringUtil.isBlank(csvUploadInfo.getHeaderZh())) {
                    //中文表头不为空，从页面获取；英文表头从文件获取
                    headers_zh = csvUploadInfo.getHeaderZh().split(",");
                    //获取第一行数据
                    InputStreamReader isr = new InputStreamReader(new FileInputStream(csvFiles.get(0)), encode);
                    BufferedReader br = new BufferedReader(isr);
                    String lineTxt = null;
                    while((lineTxt = br.readLine()) != null){
                        headers = CSVUtils.parseLine(lineTxt).toArray(new String[]{});
                        start_index = lineTxt.getBytes().length + 2;
                        break;
                    }
                    br.close();
                    isr.close();
                } else {
                    //都为空，CSV文件里含有中英文表头
                    InputStreamReader isr = new InputStreamReader(new FileInputStream(csvFiles.get(0)), encode);
                    BufferedReader br = new BufferedReader(isr);
                    String lineTxt = null;
                    int count = 1;
                    while((lineTxt = br.readLine()) != null){
                        if (count == 1) {
                            headers = CSVUtils.parseLine(lineTxt).toArray(new String[]{});
                            start_index += lineTxt.getBytes().length + 2;
                        }
                        if (count == 2) {
                            headers_zh = CSVUtils.parseLine(lineTxt).toArray(new String[]{});
                            start_index += lineTxt.getBytes().length + 2;
                            break;
                        }
                        count ++;
                    }
                    br.close();
                    isr.close();
                }
            }

            headerURI = processHeader(nick.toLowerCase() + "_" + csvUploadInfo.getClassLabelEn().toLowerCase(), headers, new URIImpl(baseUri));

            String path = Settings.graphPath + File.separator + nick + File.separator + csvUploadInfo.getClassLabelZh();
            File directory = new File(path);
            if (directory.mkdirs()) {}

            //生成OwlModel信息
            Model owl_model = ModelFactory.createDefaultModel();
            MultiThreadExtractor.addGraphStatements(owl_model, ctx);
            MultiThreadExtractor.addNodeStatements(owl_model, ctx);
            MultiThreadExtractor.generateOWLStatements(owl_model, headerURI, headers, headers_zh, ctx);

            //保存owl本体文件
//            OutputStream f = new FileOutputStream(path + File.separator + csvUploadInfo.getClassLabelZh() + ".owl");
//            owl_model.write(f, "RDF/XML"); //写入本体文件
            Upload upload = new Upload(Settings.graphDriver, Settings.graphHost, Settings.graphUsername, Settings.graphPassword);
            upload.convert2RDF(owl_model);

            for (int i=0; i<csvFiles.size(); i++) {
                File file = csvFiles.get(i);
                String name = file.getName().substring(0, file.getName().lastIndexOf("."));
                File directory_data = new File(path + File.separator + name);
                if (directory_data.mkdirs()) {}
                //生成数据model
                BigFileReader.Builder builder = new BigFileReader.Builder(file.getPath(), new IHandle() {
                    @Override
                    public void handle(String line) {
                    }
                });
                builder.withThreadSize(Settings.graphThreadNum)
                        .withCharset(encode)
                        .withHeaderURIs(headerURI)
                        .withDirectory(path + File.separator + name)
                        .withBlockSize(Settings.graphBlockSize)
                        .withInstanceURI(instanceUri)
                        .withClassURI(classUri)
                        .withBufferSize(1024 * 1024);
                BigFileReader bigFileReader = builder.build();
                Map<String, Boolean> statusMap = new HashMap<>();
                bigFileReader.start(start_index, statusMap);
                while (statusMap.get("finished") == null || !statusMap.get("finished")) {
                    Thread.sleep(1000);
                }
                file.delete();
            }
            String agentUri = "personal/" + nick.substring(0, 1).toUpperCase() + nick.substring(1);
            int agentId = 0;
            List<Agent> agents = agentService.getAgentByUri(agentUri);
            if (agents == null || agents.size() <= 0) {
                Agent agent = new Agent();
                agent.setAuthor(nick);
                agent.setBelongs("admin");
                agent.setHomePage("www.usources.cn");
                agent.setAgentUri(agentUri);
                agent.setBriefIntro(nick+"上传的个人用户数据集");
                agent.setDomain("个人");
                agent.setTitle(nick+"的数据集");
                agent.setDataStar(4);
                agent.setVersion("1.0");
                agent.setImgUrl("upload/personal_data.jpg");
                agentService.insertSelective(agent);
                agentId = agent.getId();
            }else{
            	agentId = agents.get(0).getId();
            }
            //update index
            this.updateIndex(agentId);
            return Constants.AjaxCode.SUCCESS;
        } catch (Exception e) {
            logger.info("\n=====[  csv上傳失敗  ]=====\n{}", StringUtil.getExceptonStack(e));
        }
        return Constants.AjaxCode.ERROR_UNKOWN;
    }

    public static URI[] processHeader(String className, String[] header, URI documentURI) {
        URI[] result = new URI[header.length];
        int index = 0;
        for (String h : header) {
            String candidate = h.trim();
            if (RDFUtils.isAbsoluteURI(candidate)) {
                result[index] = new URIImpl(candidate);
            } else {
                result[index] = normalize(className + "_" + candidate, documentURI);
            }
            index++;
        }
        return result;
    }

    private static URI normalize(String toBeNormalized, URI documentURI) {
        toBeNormalized = toBeNormalized.trim().toLowerCase().replace("?", "").replace("&", "");

        StringBuilder result = new StringBuilder(documentURI.toString() + "/");

        StringTokenizer tokenizer = new StringTokenizer(toBeNormalized, " ");
        while (tokenizer.hasMoreTokens()) {
            String current = tokenizer.nextToken();

            result.append(current + "_");
        }

        //Construct property rule
        if (result.toString().endsWith("_")) {
            result = new StringBuilder(result.toString().substring(0, result.toString().length() - 1));
        }

        return new URIImpl(result.toString());
    }

    private String uploadOwlFile(String baseUri, String filePath) {
        String result = null;
        try {
            File file = new File(filePath);
            if (!file.exists() || !file.isFile()) {
                throw new IOException("文件不存在");
            }
            URL urlObj = new URL("http://localhost:8180/LSH/receiveOwlFile");
            HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();
            conn.setRequestMethod("POST"); // 以Post方式提交表单，默认get方式
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setUseCaches(false); // post方式不能使用缓存
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("Charset", "UTF-8");
            // 设置边界
            String BOUNDARY = "THIS-IS-BOUNDARY-" + System.currentTimeMillis();
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
            StringBuilder sb = new StringBuilder();
            sb.append("--"); // 必须多两道线
            sb.append(BOUNDARY);
            sb.append("\r\n");
            sb.append("Content-Disposition: form-data;name=\"owlFile\";filename=\"" + baseUri.replace("/", "#").replace(":", "$") + "_" + file.getName() + "\"\r\n");
            sb.append("Content-Transfer-Encoding: binary\r\n");
            sb.append("Content-Type:application/octet-stream\r\n\r\n");
            byte[] head = sb.toString().getBytes("utf-8");
            // 获得输出流
            OutputStream out = new DataOutputStream(conn.getOutputStream());
            // 输出表头
            out.write(head);
            // 文件正文部分
            // 把文件已流文件的方式 推入到url中
            DataInputStream in = new DataInputStream(new FileInputStream(file));
            int bytes = 0;
            byte[] bufferOut = new byte[1024];
            while ((bytes = in.read(bufferOut)) != -1) {
                out.write(bufferOut, 0, bytes);
            }
            in.close();
            // 结尾部分
            byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定义最后数据分隔线
            out.write(foot);
            StringBuffer buffer = new StringBuffer();
            BufferedReader reader = null;
            try {
                // 定义BufferedReader输入流来读取URL的响应
                reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                String line = null;
                while ((line = reader.readLine()) != null) {
                    buffer.append(line);
                }
                if (result == null) {
                    result = buffer.toString();
                }
            } catch (IOException e) {
                System.out.println("发送POST请求出现异常！" + e);
                e.printStackTrace();
                throw new IOException("数据读取异常");
            } finally {
                if (reader != null) {
                    reader.close();
                }
                out.flush();
                out.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    
    //Update the index of agent
    private void updateIndex(int agentId) {
    	NetWorkContext context = NetWorkContext.get("http://www.usources.cn:8080/index/updateAgentIndex?agentId="+agentId);
        NetWorkContext.Response response = HttpRequest.doReq(context);
        
    }
}